package com.sinn.gitrepobranchcompare.application;

import com.sinn.gitrepobranchcompare.config.GitRepoScanConfig;
import com.sinn.gitrepobranchcompare.config.GitRepoScanResult;
import com.sinn.gitrepobranchcompare.tool.Tool;
import com.sun.net.httpserver.HttpServer;

import java.awt.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.List;

public class GitOperation {

    private final List<GitRepoScanResult> repoScanResults = new ArrayList<>();

    public void pull() {
        GitRepoScanConfig config = GitRepoScanConfig.getGitRepoScanConfig();
        System.out.println("开始拉取代码库");
        String repoBaseUrl = config.getOnlineLink();
        for (String repoName: config.getRepoList()) {
            GitRepoScanResult result = new GitRepoScanResult();

            result.setAppName(config.getAppName());
            result.setRepoName(repoName);
            result.setLocalDir(config.getLocalDir());
            result.setTempDir(config.getTempDir());
            result.setResultDir(config.getResultDir());

            String repoUrl = repoBaseUrl + repoName;
            if ("Gitee".equals(config.getRepoType())) {
                repoUrl = repoUrl + ".git";
            }
            result.setRepoUrl(repoUrl);

            boolean sourceFlag = pullGitRepoByBranch(repoUrl, config.getSourceBranch(), "source", config);
            result.setSourceFlag(sourceFlag);
            if (!sourceFlag) {
                System.out.println("代码库源分支拉取失败，任务结束");
            }

            boolean targetFlag = false;
            boolean greyFlag = false;
            if (!config.getTargetBranch().isEmpty()) {
                targetFlag = pullGitRepoByBranch(repoUrl, config.getTargetBranch(), "target", config);
                result.setTargetFlag(targetFlag);
            }

            if (!config.getGreyBranch().isEmpty()) {
                greyFlag = pullGitRepoByBranch(repoUrl, config.getGreyBranch(), "grey", config);
                result.setGreyFlag(greyFlag);
            }

            if (!targetFlag && !greyFlag) {
                System.out.println("代码库目标分支与灰度分支拉取失败，任务结束");
                return;
            }

            repoScanResults.add(result);
        }
        System.out.println("拉取代码库完成");
    }

    private boolean pullGitRepoByBranch(String repoUrl, String branch, String type, GitRepoScanConfig config) {
        System.out.println("拉取代码库：" + repoUrl + ", 分支：" + branch + ", 类型：" + type + " 开始。");
        if (branch.isEmpty()) {
            branch = "master";
        }
        String localDir = config.getLocalDir();
        String repoName = repoUrl.replace(config.getOnlineLink(), "").replace(".git", "");
        try {
            ProcessBuilder processBuilder = new ProcessBuilder("git", "clone", repoUrl, localDir + File.separator + type +
                    File.separator + repoName, "--branch", branch, "--single-branch");
            Process process = processBuilder.start();
            process.waitFor();
            if (process.exitValue() != 0) {
                System.out.println("拉取代码库：" + repoUrl + ", 分支：" + branch + ", 类型：" + type + " 失败。");
                return false;
            }
            System.out.println("拉取代码库：" + repoUrl + ", 分支：" + branch + ", 类型：" + type + " 成功。");
            return true;
        } catch (Exception e) {
            System.out.println("拉取代码库：" + repoUrl + ", 分支：" + branch + ", 类型：" + type + " 异常。");
            e.printStackTrace();
            return false;
        }
    }

    public void changeGitIndex() {
        System.out.println("迁移 .git 文件到 temp 目录下开始");
        // 先迁移 .git 文件夹到 temp 目录下
        moveGitIndexToTemp();
        System.out.println("迁移 .git 文件到 temp 目录下完成");
        System.out.println("迁移 .git 文件到 target 和 grey 目录下开始");
        // 然后拷贝 .git 文件到各个target 目录下和 grey 目录下
        copyGitIndexToLocal();
        System.out.println("迁移 .git 文件到 target 和 grey 目录下完成");
    }

    private void moveGitIndexToTemp() {
        for (GitRepoScanResult result: repoScanResults) {
            if (!result.isSourceFlag()) {
                return;
            }
            String localDir = result.getLocalDir();
            String tempDir = result.getTempDir();
            String srcDirSource = localDir + File.separator + "source" + File.separator + result.getRepoName() + File.separator + ".git";
            String destDirSource = tempDir + File.separator + "source" + File.separator + result.getRepoName();
            Tool.moveGitIndexFile(srcDirSource, destDirSource);

            if (result.isTargetFlag()) {
                String srcDirTarget = localDir + File.separator + "target" + File.separator + result.getRepoName() + File.separator + ".git";
                String destDirTarget = tempDir + File.separator + "target" + File.separator + result.getRepoName();
                Tool.moveGitIndexFile(srcDirTarget, destDirTarget);
            }

            if (result.isGreyFlag()) {
                String srcDirGrey = localDir + File.separator + "grey" + File.separator + result.getRepoName() + File.separator + ".git";
                String destDirGrey = tempDir + File.separator + "grey" + File.separator + result.getRepoName();
                Tool.moveGitIndexFile(srcDirGrey, destDirGrey);
            }
        }
    }

    private void copyGitIndexToLocal() {
        for (GitRepoScanResult result: repoScanResults) {
            String localDir = result.getLocalDir();
            String tempDir = result.getTempDir();
            String srcDirSource = tempDir + File.separator + "source" + File.separator + result.getRepoName();
            String destDirTarget = localDir + File.separator + "target" + File.separator + result.getRepoName();
            String destDirGrey = localDir + File.separator + "grey" + File.separator + result.getRepoName();
            if (result.isTargetFlag()) {
                Tool.copyGitIndexFile(srcDirSource, destDirTarget);
            }
            if (result.isGreyFlag()) {
                Tool.copyGitIndexFile(srcDirSource, destDirGrey);
            }
        }
    }

    public void generateGitChangeReport() {
        System.out.println("生成 git 变动记录开始");
        System.out.println("生成 git 变动记录 - target");
        generateGitChangeReportTarget();
        System.out.println("生成 git 变动记录 - grey");
        generateGitChangeReportGrey();
        System.out.println("生成 git 变动记录 - target 和 grey");
        generateGitChangeReportBetweenTargetAndGrey();
        System.out.println("生成 git 变动记录结束");
    }

    private void generateGitChangeReportTarget() {
        for (GitRepoScanResult result: repoScanResults) {
            if (!result.isTargetFlag()) {
                System.out.println("当前代码库无需比对 target 分支变动, " + result.getRepoUrl());
                continue;
            }
            // git status -s, 输出全量的git变动清单
            System.out.println("获取变动记录文件清单");
            List<Map<String, String>> changedFileList = getGitStatusResult(result, "target");
            // git diff --status filename , 统计单个文件的改动情况
            System.out.println("获取文件变动概要");
            getGitDiffStatus(result, "target", changedFileList);
            // git diff filename | sed 's/$/<br>/', 统计单个文件的改动内容
            System.out.println("获取变动记录明细");
            getGitDiffAll(result, "target", changedFileList);
            // git log --pretty=format:"%an<%ae> %ad %s" --date=format:"%Y-%m-%d %H:%M:S" filename
            System.out.println("获取变动日志");
            getGitLogAll(result, "target", changedFileList);
        }
    }

    private void generateGitChangeReportGrey() {
        for (GitRepoScanResult result: repoScanResults) {
            if (!result.isGreyFlag()) {
                System.out.println("当前代码库无需比对 grey 分支变动, " + result.getRepoUrl());
                continue;
            }
            System.out.println("获取变动记录文件清单");
            List<Map<String, String>> changedFileList = getGitStatusResult(result, "grey");
            System.out.println("获取文件变动概要");
            getGitDiffStatus(result, "grey", changedFileList);
            System.out.println("获取变动记录明细");
            getGitDiffAll(result, "grey", changedFileList);
            System.out.println("获取变动日志");
            getGitLogAll(result, "grey", changedFileList);
        }
    }

    private void generateGitChangeReportBetweenTargetAndGrey() {
        for (GitRepoScanResult result: repoScanResults) {
            if (!result.isGreyFlag() || !result.isTargetFlag()) {
                System.out.println("当前代码库 grey 分支或 target 分支无配置，无需检测, " + result.getRepoUrl());
                continue;
            }
            // 清理配置，将 target 目录下的 .git 文件清理
            // 将 grey 目录下的 .git 文件迁移到 target 目录下

            String localDir = result.getLocalDir();
            String tempDir = result.getTempDir();
            String destDirSource = localDir + File.separator + "target" + File.separator + result.getRepoName();
            String srcDirSource = tempDir + File.separator + "grey" + File.separator + result.getRepoName();
            Tool.deleteFileOrDirectory(destDirSource + File.separator + ".git");
            Tool.copyGitIndexFile(srcDirSource, destDirSource);

            System.out.println("获取变动记录文件清单");
            List<Map<String, String>> changedFileList = getGitStatusResult(result, "greyTarget");
            System.out.println("获取文件变动概要");
            getGitDiffStatus(result, "greyTarget", changedFileList);
            System.out.println("获取变动记录明细");
            getGitDiffAll(result, "greyTarget", changedFileList);
            System.out.println("获取变动日志");
            getGitLogAll(result, "greyTarget", changedFileList);
        }
    }

    /**
     * Description: git status -s
     */
    private List<Map<String, String>> getGitStatusResult(GitRepoScanResult result, String type) {

        String prefixType = "";
        if (type.equals("grey")) {
            prefixType = "source -> grey";
        } else if (type.equals("target")) {
            prefixType = "source -> target";
        } else {
            prefixType = "grey -> target";
        }

        String preFix = type;
        if (type.equals("greyTarget")) {
            preFix = "target";
        }
        String repoDir = result.getLocalDir() + File.separator + preFix + File.separator + result.getRepoName();
        List<Map<String, String>> changedFileList = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new ProcessBuilder("cmd", "/c", "cd", repoDir, "&", "git", "status", "-s", repoDir)
                        .redirectErrorStream(true)
                        .start().getInputStream()
        ))) {
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                String GitOperType = line.split(" ")[0];
                if (GitOperType.startsWith("D")) {
                    GitOperType = "D";
                } else if (GitOperType.startsWith("M")) {
                    GitOperType = "M";
                } else {
                    GitOperType = "A";
                }
                String fileName = line.split(" ")[1];
                String sha256 = Tool.generateSHA256(fileName);
                if (line.startsWith("M ")) {
                    Map<String, String> map = new HashMap<>();
                    map.put("filename", fileName);
                    map.put("sha256", sha256);
                    changedFileList.add(map);
                }

                sb.append(result.getAppName()).append("\t");
                sb.append(result.getRepoName()).append("\t");
                sb.append(prefixType).append("\t");
                sb.append(GitOperType).append("\t");
                sb.append(fileName).append("\t");
                sb.append(sha256).append("\r\n");
            }

            String outputFile = result.getResultDir() + File.separator + result.getRepoName().replaceAll("/", "_") + "_" + type + "_change_list_"
                    + new Date().getTime() + ".txt";
            Tool.appendToFile(outputFile, sb);
        } catch (Exception e) {
            changedFileList = null;
            e.printStackTrace();
        }

        return changedFileList;
    }

    /**
     * Description: git diff --stat filename
     * output: vue_mogu_web/static/css/index.css  | 98 +++++++++++++++++++++------------------
     * command: git diff --numstat .\vue_mogu_web\static\css\index.css
     * output: 52      46      vue_mogu_web/static/css/index.css
     *
     */
    private void getGitDiffStatus(GitRepoScanResult result, String type, List<Map<String, String>> fileList) {
        String preFix = type;
        if (type.equals("greyTarget")) {
            preFix = "target";
        }
        String repoDir = result.getLocalDir() + File.separator + preFix + File.separator + result.getRepoName();

        for (Map<String, String> map: fileList) {
            String filename = map.get("filename");
            String sha256 = map.get("sha256");

            String fullFileName = repoDir + File.separator + filename;
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                    new ProcessBuilder("cmd", "/c", "cd", repoDir, "&", "git", "diff", "--stat", fullFileName)
                            .redirectErrorStream(true)
                            .start().getInputStream()
            ))) {
                String line;
                StringBuilder sb = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    sb.append(line).append("\r\n");
                }

                String outputFile = result.getResultDir() + File.separator + result.getRepoName() + File.separator
                        + type + File.separator + "diff" + File.separator
                        + sha256 + ".txt";
                Tool.appendToFile(outputFile, sb);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    /**
     * Description: git diff filename | sed 's/$/<br>/'
     * 结果写入到html文本内容
     */
    private void getGitDiffAll(GitRepoScanResult result, String type, List<Map<String, String>> fileList) {
        String preFix = type;
        if (type.equals("greyTarget")) {
            preFix = "target";
        }
        String repoDir = result.getLocalDir() + File.separator + preFix + File.separator + result.getRepoName();

        for (Map<String, String> map: fileList) {
            String filename = map.get("filename");
            String sha256 = map.get("sha256");

            String fullFileName = repoDir + File.separator + filename;
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                    new ProcessBuilder("cmd", "/c", "cd", repoDir, "&", "git", "diff", fullFileName)
                            .redirectErrorStream(true)
                            .start().getInputStream()
            ))) {
                String line;
                StringBuilder sb = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    sb.append(line).append("\r\n");
                }

                String outputFile = result.getResultDir() + File.separator + result.getRepoName() + File.separator
                        + type + File.separator + "detail" + File.separator
                        + sha256 + ".txt";
                Tool.appendToFile(outputFile, sb);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * Description: git log --pretty=format:"%an<%ae> %ad %s" --date=format:"%Y-%m-%d %H:%M:%S" filename
     */
    private void getGitLogAll(GitRepoScanResult result, String type, List<Map<String, String>> fileList) {
        String preFix = type;
        if (type.equals("greyTarget")) {
            preFix = "target";
        }
        String repoDir = result.getLocalDir() + File.separator + preFix + File.separator + result.getRepoName();

        for (Map<String, String> map: fileList) {
            String filename = map.get("filename");
            String sha256 = map.get("sha256");

            String fullFileName = repoDir + File.separator + filename;
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                    new ProcessBuilder("cmd", "/c", "cd", repoDir, "&", "git", "log", "--pretty=format:%an<%ae> %ad %s", "--date=format:%Y-%m-%d %H:%M:%S", fullFileName)
                            .redirectErrorStream(true)
                            .start().getInputStream()
            ))) {

                String line;
                StringBuilder sb = new StringBuilder();
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    sb.append(line).append("\r\n");
                }

                String outputFile = result.getResultDir() + File.separator + result.getRepoName() + File.separator
                        + type + File.separator + "log" + File.separator
                        + sha256 + ".txt";
                Tool.appendToFile(outputFile, sb);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void generateHTML() {
        try {
            System.out.println("生成HTML报告开始");
            GitRepoScanConfig config = GitRepoScanConfig.getGitRepoScanConfig();
            // 设置资源根目录
            String resourceDir = config.getResultDir(); // 替换为实际路径
            HtmlServer htmlServer = new HtmlServer();

            int port = 8000; // HTTP 服务器端口

            // 读取所有符合命名规则的 TXT 文件
            List<File> txtFiles = htmlServer.findTxtFiles(resourceDir);

            // 为每个 TXT 文件生成 HTML 文件
            List<String> htmlFileNames = new ArrayList<>();
            for (File txtFile : txtFiles) {
                String htmlContent = htmlServer.generateHtml(txtFile);
                String htmlFileName = txtFile.getName().replace(".txt", ".html");
                Path htmlFilePath = Paths.get(resourceDir, htmlFileName);
                Files.write(htmlFilePath, htmlContent.getBytes());
                htmlFileNames.add(htmlFileName);
                System.out.println("生成 HTML 文件: " + htmlFilePath);
            }

            // 生成 index.html 文件
            htmlServer.generateIndexHtml(resourceDir, htmlFileNames);

            System.out.println("生成HTML报告结束");

            System.out.println("打开HTML报告...");
            // 启动 HTTP 服务器
            HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
            server.createContext("/", new HtmlServer.FileHandler(resourceDir));
            // 注册静态资源处理器
            server.createContext("/static", new HtmlServer.StaticResourceHandler());
            server.setExecutor(null); // 使用默认的线程池
            server.start();
            System.out.println("HTTP 服务器已启动，访问地址: http://localhost:" + port);

            // 打开浏览器
            if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
                Desktop.getDesktop().browse(new URI("http://localhost:" + port + "/index.html"));
            } else {
                System.err.println("无法打开浏览器，请手动访问: http://localhost:" + port + "/index.html");
            }
        } catch (Exception e) {
            System.out.println("html文件处理失败");
            e.printStackTrace();
        }

    }

}
